﻿#region License
/* Copyright (C) 2009 Tim Coulter
 * 
 * This file is part of ExtremeML.
 * 
 * ExtremeML is free software: you can redistribute
 * it and/or modify it under the terms of the GNU General Public
 * License, as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * 
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/.
*/
#endregion

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;

namespace ExtremeML.Sdk.Gdi
{
	/// <summary>
	/// Exposes static library methods that support image manipulation.
	/// </summary>
	internal static class Imaging
	{
		/// <summary>
		/// Returns a copy of the passed image, enabling its stream to be disposed.
		/// </summary>
		/// <param name="image">An <see cref="Image"/> from which to populate the new instance.</param>
		/// <returns>A new <see cref="Bitmap"/> instance.</returns>
		internal static Bitmap CopyImage(
			Image image)
		{
			var result = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb);
			result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
			var graphics = Graphics.FromImage(result);
			graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
			graphics.DrawImage(image, 0, 0);
			graphics.Dispose();
			AssignImageFormat(ref result, image.RawFormat);
			return result;
		}

		/// <summary>
		/// Assigns the specified <see cref="ImageFormat"/> to the passed <see cref="Bitmap"/>.
		/// </summary>
		/// <param name="image">An <see cref="Image"/> to which a format must be assigned.</param>
		/// <param name="format">An <see cref="ImageFormat"/> that specifies the format to assign.</param>
		internal static void AssignImageFormat(
			ref Bitmap image,
			ImageFormat format)
		{
			if (image == null) throw new ArgumentNullException("image");
			if (format == null) throw new ArgumentNullException("format");

			var result = new Bitmap(SaveImageToStream(image, format, 100));

			image.Dispose();
			image = result;
		}

		/// <summary>
		/// Returns a stream containing the content of the passed image, saved in the specified format and quality.
		/// </summary>
		/// <param name="image">An <see cref="Image"/> to save.</param>
		/// <param name="format">An <see cref="ImageFormat"/> that specifies the format in which to save the image.</param>
		/// <param name="quality">An <see cref="int"/> that specifies the compression quality for Jpeg images.</param>
		/// <returns>A <see cref="Stream"/> containing the data from the passed image.</returns>
		internal static MemoryStream SaveImageToStream(
			Image image,
			ImageFormat format,
			int quality)
		{
			if (image == null) throw new ArgumentNullException("image");
			if (format == null) throw new ArgumentNullException("format");

			var stream = new MemoryStream();
			image.Save(stream, GetImageCodecInfo(format), GetEncoderParameters(quality));
			return stream;
		}

		/// <summary>
		/// Returns the <see cref="ImageCodecInfo"/> corresponding to the passed <see cref="ImageFormat"/>.
		/// </summary>
		/// <param name="format">An <see cref="ImageFormat"/> to resolve.</param>
		/// <returns>An <see cref="ImageCodecInfo"/> that represents the specified image format.</returns>
		internal static ImageCodecInfo GetImageCodecInfo(
			ImageFormat format)
		{
			var mimeType = GetImageMimeType(format);

			foreach (var codec in ImageCodecInfo.GetImageEncoders())
			{
				if (codec.MimeType == mimeType)
				{
					return codec;
				}
			}
			return null;
		}

		/// <summary>
		/// Returns the Codec for the specified image mime type.
		/// </summary>
		/// <param name="mimeType">A <see cref="string"/> that specifies the mime type of the required Codec.</param>
		/// <returns>An <see cref="ImageCodecInfo"/> that represents the specified Codec.</returns>
		internal static ImageCodecInfo GetImageCodecInfo(
			string mimeType)
		{
			foreach (var codec in ImageCodecInfo.GetImageEncoders())
			{
				if (codec.MimeType == mimeType)
				{
					return codec;
				}
			}

			throw new ArgumentException(string.Format("No Codec exists for mime type {0}.", mimeType));
		}

		/// <summary> 
		/// Returns a MIME Type string corresponding to the passed ImageFormat. 
		/// </summary> 
		/// <param name="format">An <see cref="ImageFormat"/> to resolve.</param>
		/// <returns>A <see cref="string"/> that contains the mime type for the specified image format.</returns>
		internal static string GetImageMimeType(
			ImageFormat format)
		{
			if (format.Guid == ImageFormat.Bmp.Guid)
			{
				return MimeTypeConstants.ImageBmp;
			}
			else if (format.Guid == ImageFormat.Jpeg.Guid)
			{
				return MimeTypeConstants.ImageJpeg;
			}
			else if (format.Guid == ImageFormat.Gif.Guid)
			{
				return MimeTypeConstants.ImageGif;
			}
			else if (format.Guid == ImageFormat.Tiff.Guid)
			{
				return MimeTypeConstants.ImageTiff;
			}
			else if (format.Guid == ImageFormat.Emf.Guid)
			{
				return MimeTypeConstants.ImageEmf;
			}
			else if (format.Guid == ImageFormat.Exif.Guid)
			{
				return MimeTypeConstants.ImageExif;
			}
			else if (format.Guid == ImageFormat.Icon.Guid)
			{
				return MimeTypeConstants.ImageIcon;
			}
			else if (format.Guid == ImageFormat.Png.Guid)
			{
				return MimeTypeConstants.ImagePng;
			}
			else if (format.Guid == ImageFormat.Wmf.Guid)
			{
				return MimeTypeConstants.ImageWmf;
			}
			else
			{
				return MimeTypeConstants.ImageUknown;
			}
		}

		/// <summary>
		/// Returns a set of encoder parameters that enforce the specified image quality.
		/// </summary>
		/// <param name="quality">An <see cref="int"/> that specifies the required compression quality.</param>
		/// <returns>A new <see cref="EncoderParameters"/> instance.</returns>
		internal static EncoderParameters GetEncoderParameters(
			int quality)
		{
			quality = quality <= 100 ? quality : 100;
			quality = quality >= 0 ? quality : 0;

			var result = new EncoderParameters(1);
			result.Param[0] = new EncoderParameter(Encoder.Quality, quality);
			return result;
		}

		/// <summary> 
		/// Returns a <see cref="Size"/> representing the dimensions of the
		/// passed image, after applying the specified scaling parameters.
		/// </summary> 
		/// <param name="image">An <see cref="Image"/> whose dimensions are to be scaled.</param>
		/// <param name="width">A nullable <see cref="int"/> that specifies the target image width.</param>
		/// <param name="height">A nullable <see cref="int"/> that specifies the target image height.</param>
		/// <returns>A new <see cref="Size"/> that represents the scaled image dimensions.</returns>
		internal static Size GetScaledImageSize(
			Image image,
			int? width,
			int? height)
		{
			if (image == null) throw new ArgumentNullException("image");

			if (!width.HasValue && !height.HasValue)
			{
				// Return the original image un-scaled
				return new Size(image.Width, image.Height);
			}
			else if (!width.HasValue)
			{
				// Scale the image using the specified height 
				return new Size((int)(image.Width / ((float)image.Height / height)), height.Value);
			}
			else if (!height.HasValue)
			{
				// Scale the image using the specified width 
				return new Size(width.Value, (int)(image.Height / ((float)image.Width / width)));
			}
			else
			{
				// Distort the image to the specified width and height 
				return new Size(width.Value, height.Value);
			}
		}
	}
}